//window.requestAnimationFrame = (function () {
//   return window.requestAnimationFrame ||
//         window.webkitRequestAnimationFrame ||
//         window.mozRequestAnimationFrame ||
//         function (callback) {
//             window.setTimeout(callback, 1000 / 60);
//         };
//     })();
var SoftRenderer;
(function (SoftRenderer) {
    (function (RasterizeMode) {
        RasterizeMode[RasterizeMode["Wireframe"] = 0] = "Wireframe";
        RasterizeMode[RasterizeMode["Triangle"] = 1] = "Triangle";
    })(SoftRenderer.RasterizeMode || (SoftRenderer.RasterizeMode = {}));
    var RasterizeMode = SoftRenderer.RasterizeMode;
    ;
    var Renderer = (function () {
        function Renderer(renderTarget) {
            this.renderTarget = renderTarget;
            this.rasterizeMode = 1 /* Triangle */;
            this.cameras = new Array();
            this.meshes = new Array();
            this.lightes = new Array();
        }
        Renderer.prototype.render = function (viewingCamera) {
            var viewMatrix = BABYLON.Matrix.LookAtLH(viewingCamera.position, viewingCamera.lookAt, BABYLON.Vector3.Up());
            var projectionMatrix;
            if (viewingCamera.mode == 0 /* Perspective */) {
                projectionMatrix = BABYLON.Matrix.PerspectiveFovLH(viewingCamera.fov, this.renderTarget.width / this.renderTarget.height, viewingCamera.znear, viewingCamera.zfar);
            }
            else {
                projectionMatrix = BABYLON.Matrix.OrthoLH(this.renderTarget.width / this.renderTarget.height * viewingCamera.size, viewingCamera.size, viewingCamera.znear, viewingCamera.zfar);
            }
            for (var i = 0; i < this.meshes.length; i++) {
                var mesh = this.meshes[i];
                // Beware to apply scale, rotation before translation 
                var scaleMat = BABYLON.Matrix.Scaling(mesh.scale.x, mesh.scale.y, mesh.scale.z);
                var rotateMat = BABYLON.Matrix.RotationYawPitchRoll(mesh.rotation.y, mesh.rotation.x, mesh.rotation.z);
                var translateMat = BABYLON.Matrix.Translation(mesh.position.x, mesh.position.y, mesh.position.z);
                var worldMatrix = scaleMat.multiply(rotateMat).multiply(translateMat);
                var mvp = worldMatrix.multiply(viewMatrix).multiply(projectionMatrix);
                for (var j = 0; j < mesh.faces.length; j++) {
                    var face = mesh.faces[j];
                    var vertexA = mesh.vertices[face.a];
                    var vertexB = mesh.vertices[face.b];
                    var vertexC = mesh.vertices[face.c];
                    var pointA = this.project(vertexA, mvp);
                    var pointB = this.project(vertexB, mvp);
                    var pointC = this.project(vertexC, mvp);
                    var colorA = mesh.colors[face.a];
                    var colorB = mesh.colors[face.b];
                    var colorC = mesh.colors[face.c];
                    switch (this.rasterizeMode) {
                        case 0 /* Wireframe */:
                            {
                                this.rasterizeLine(pointA, pointB, colorA, colorB);
                                this.rasterizeLine(pointB, pointC, colorB, colorC);
                                this.rasterizeLine(pointC, pointA, colorC, colorA);
                                break;
                            }
                        case 1 /* Triangle */:
                            {
                                this.rasterizeTriangle(pointA, pointB, pointC, colorA, colorB, colorC);
                                break;
                            }
                    }
                }
            }
        };
        Renderer.prototype.clamp = function (value, min, max) {
            if (min === void 0) { min = 0; }
            if (max === void 0) { max = 1; }
            return Math.max(min, Math.min(value, max));
        };
        Renderer.prototype.interpolate = function (from, to, t) {
            return from + (to - from) * this.clamp(t);
        };
        Renderer.prototype.project = function (point, mvp) {
            // transforming the coordinates
            var p = BABYLON.Vector3.TransformCoordinates(point, mvp);
            // The transformed coordinates will be based on coordinate system
            // starting on the center of the screen. But drawing on screen normally starts
            // from top left. We then need to transform them again to have x:0, y:0 on top left.
            var x = (p.x * 0.5 + 0.5) * this.renderTarget.width;
            var y = this.renderTarget.height - (p.y * 0.5 + 0.5) * this.renderTarget.height;
            return new BABYLON.Vector3(x, y, p.z);
        };
        Renderer.prototype.rasterizeLine = function (p0, p1, c0, c1) {
            // convert float to integer
            var x0 = p0.x >> 0;
            var y0 = p0.y >> 0;
            var x1 = p1.x >> 0;
            var y1 = p1.y >> 0;
            // integer method
            var dx = Math.abs(x1 - x0);
            var dy = Math.abs(y1 - y0);
            var sx = (x0 < x1) ? 1 : -1;
            var sy = (y0 < y1) ? 1 : -1;
            var err = dx - dy;
            while (true) {
                var z;
                var c;
                if (dx > dy) {
                    var t = (x0 - p0.x) / (p1.x - p0.x);
                    z = this.interpolate(p0.z, p1.z, t);
                    c = BABYLON.Color4.Lerp(c0, c1, t);
                }
                else {
                    var t = (y0 - p0.y) / (p1.y - p0.y);
                    z = this.interpolate(p0.z, p1.z, t);
                    c = BABYLON.Color4.Lerp(c0, c1, t);
                }
                this.renderTarget.putPixel(x0, y0, z, c);
                if ((x0 == x1) && (y0 == y1))
                    break;
                var e2 = 2 * err;
                if (e2 > -dy) {
                    err -= dy;
                    x0 += sx;
                }
                if (e2 < dx) {
                    err += dx;
                    y0 += sy;
                }
            }
            // // float method
            // var x0 = p0.x >> 0;
            // var y0 = p0.y >> 0;
            // var x1 = p1.x >> 0;
            // var y1 = p1.y >> 0;
            // var dx = Math.abs(x1 - x0);
            // var dy = Math.abs(y1 - y0);
            // var sx = (x0 < x1) ? 1 : -1;
            // var sy = (y0 < y1) ? 1 : -1;
            // var err = 0;
            // var delta = dy / dx;
            // for (; x0 != x1; x0 += sx)
            // {
            //     var z:number;
            //     var c:BABYLON.Color4;
            //     if (dx > dy) {
            //         var t = (x0 - p0.x) / (p1.x - p0.x);
            //         z = this.interpolate(p0.z, p1.z, t);
            //         c = BABYLON.Color4.Lerp(c0, c1, t);
            //     }
            //     else {
            //         var t = (y0 - p0.y) / (p1.y - p0.y);
            //         z = this.interpolate(p0.z, p1.z, t);
            //         c = BABYLON.Color4.Lerp(c0, c1, t);
            //     }
            //     this.renderTarget.putPixel(x0, y0, z, c);
            //     var err = err + delta;
            //     while (err >= 0.5)
            //     {
            //         if (dx > dy) {
            //             var t = (x0 - p0.x) / (p1.x - p0.x);
            //             z = this.interpolate(p0.z, p1.z, t);
            //             c = BABYLON.Color4.Lerp(c0, c1, t);
            //         }
            //         else {
            //             var t = (y0 - p0.y) / (p1.y - p0.y);
            //             z = this.interpolate(p0.z, p1.z, t);
            //             c = BABYLON.Color4.Lerp(c0, c1, t);
            //         }
            //         this.renderTarget.putPixel(x0, y0, z, c);
            //         err -= 1;
            //         y0 += sy;
            //     }
            // }
        };
        Renderer.prototype.rasterizeTriangle = function (p0, p1, p2, c0, c1, c2) {
            // Sorting the points in order to always have this order on screen p0, p1 & p2
            // with p0 always up (thus having the Y the lowest possible to be near the top screen)
            // then p1 between p0 & p2
            if (p0.y > p1.y) {
                var temp = p1;
                p1 = p0;
                p0 = temp;
                var temp2 = c1;
                c1 = c0;
                c0 = temp2;
            }
            if (p1.y > p2.y) {
                var temp = p1;
                p1 = p2;
                p2 = temp;
                var temp2 = c1;
                c1 = c2;
                c2 = temp2;
            }
            if (p0.y > p1.y) {
                var temp = p1;
                p1 = p0;
                p0 = temp;
                var temp2 = c1;
                c1 = c0;
                c0 = temp2;
            }
            // inverse slopes
            var dP0P1;
            var dP0P2;
            // http://en.wikipedia.org/wiki/Slope
            // Computing slopes
            if (p1.y - p0.y > 0)
                dP0P1 = (p1.x - p0.x) / (p1.y - p0.y);
            else
                dP0P1 = 0;
            if (p2.y - p0.y > 0)
                dP0P2 = (p2.x - p0.x) / (p2.y - p0.y);
            else
                dP0P2 = 0;
            // First case where triangles are like that:
            // P0
            // -
            // -- 
            // - -
            // -  -
            // -   - P1
            // -  -
            // - -
            // -
            // P2
            if (dP0P1 > dP0P2) {
                for (var y = (p0.y >> 0); y <= (p2.y >> 0); y++) {
                    if (y < p1.y) {
                        this.scanLine(y, p0, p2, p0, p1, c0, c2, c0, c1);
                    }
                    else {
                        this.scanLine(y, p0, p2, p1, p2, c0, c2, c1, c2);
                    }
                }
            }
            else {
                for (var y = (p0.y >> 0); y <= (p2.y >> 0); y++) {
                    if (y < p1.y) {
                        this.scanLine(y, p0, p1, p0, p2, c0, c1, c0, c2);
                    }
                    else {
                        this.scanLine(y, p1, p2, p0, p2, c1, c2, c0, c2);
                    }
                }
            }
        };
        Renderer.prototype.scanLine = function (y, pa, pb, pc, pd, ca, cb, cc, cd) {
            // Thanks to current Y, we can compute the gradient to compute others values like
            // the starting X (sx) and ending X (ex) to draw between
            // if pa.Y == pb.Y or pc.Y == pd.Y, gradient is forced to 1
            var t0 = pa.y != pb.y ? (y - pa.y) / (pb.y - pa.y) : 1;
            var t1 = pc.y != pd.y ? (y - pc.y) / (pd.y - pc.y) : 1;
            var sx = this.interpolate(pa.x, pb.x, t0) >> 0;
            var ex = this.interpolate(pc.x, pd.x, t1) >> 0;
            var sz = this.interpolate(pa.z, pb.z, t0);
            var ez = this.interpolate(pc.z, pd.z, t1);
            var sc = BABYLON.Color4.Lerp(ca, cb, t0);
            var ec = BABYLON.Color4.Lerp(cc, cd, t1);
            for (var x = sx; x < ex; x++) {
                var t = (x - sx) / (ex - sx);
                var c = BABYLON.Color4.Lerp(sc, ec, t);
                var z = this.interpolate(sz, ez, t);
                this.renderTarget.putPixel(x, y, z, c);
            }
        };
        return Renderer;
    })();
    SoftRenderer.Renderer = Renderer;
})(SoftRenderer || (SoftRenderer = {}));
